package com.zhang.boot.core.service.impl;

import cn.hutool.core.codec.Base64;
import com.google.code.kaptcha.Producer;
import com.zhang.boot.core.constants.ApiConstant;
import com.zhang.boot.core.constants.ErrorEnum;
import com.zhang.boot.core.constants.RedisConstant;
import com.zhang.boot.core.exception.BusinessException;
import com.zhang.boot.core.service.LoginService;
import com.zhang.boot.core.service.MenuService;
import com.zhang.boot.core.service.RoleService;
import com.zhang.boot.core.util.AssertUtil;
import com.zhang.boot.core.util.UUIDUtil;
import com.zhang.boot.core.util.cache.RedisUtil;
import com.zhang.boot.core.vo.LoginUser;
import com.zhang.boot.core.vo.Result;
import com.zhang.boot.core.vo.login.CaptchaVo;
import com.zhang.boot.core.vo.login.LoginRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FastByteArrayOutputStream;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService {
    private final RedisUtil redisUtil;
    private final AuthenticationManager authenticationManager;
    private final TokenService tokenService;
    private final RoleService roleService;
    private final MenuService menuService;

    @Resource(name = "captchaProducer")
    private Producer captchaProducer;

    @Resource(name = "captchaProducerMath")
    private Producer captchaProducerMath;

    // 验证码类型
    @Value("${captchaType}")
    private String captchaType;

    @Override
    public LoginUser login(LoginRequest request) {
        String username = request.getUsername();
        String password = request.getPassword();
        String code = request.getCode();
        String uuid = request.getUuid();
        String captchaKey = RedisConstant.CAPTCHA_CODE_PREFIX + uuid;
        String captcha = redisUtil.getCacheObject(captchaKey);
        AssertUtil.notEmpty(captcha, ErrorEnum.CAPTCHA_INVALID);
        redisUtil.deleteObject(captchaKey);
        if (!code.equalsIgnoreCase(captcha)) {
            //TODO 记录操作日志
            throw new BusinessException(ErrorEnum.CAPTCHA_ERROR);
        }
        Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        if (authentication == null) {
            //TODO 记录操作日志
            throw new BusinessException(ErrorEnum.ACCOUNT_ERROR);
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        String token = tokenService.createToken(loginUser);
        loginUser.setToken(token);
        return loginUser;
    }

    /**
     * TODO 该接口如果同一用户重复刷新此接口,可能导致redis缓存多个验证码
     * @return
     */
    @Override
    public Result captchaImage() {
        String uuid = UUIDUtil.simpleUUID();
        String verifyKey = RedisConstant.CAPTCHA_CODE_PREFIX + uuid;
        String capStr = null;
        String code = null;
        BufferedImage image = null;
        if (ApiConstant.MATH.equalsIgnoreCase(captchaType)) {
            String capText = captchaProducerMath.createText();
            capStr = capText.substring(0, capText.lastIndexOf("@"));
            code = capText.substring(capText.lastIndexOf("@") + 1);
            image = captchaProducerMath.createImage(capStr);
        } else {
            capStr = code = captchaProducer.createText();
            image = captchaProducer.createImage(capStr);
        }
        redisUtil.setCacheObject(verifyKey, code, RedisConstant.CAPTCHA_CODE_EXPIRATION, TimeUnit.MINUTES);
        //转换流信息写出
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        try {
            ImageIO.write(image, "jpg", os);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Result.ok(CaptchaVo.builder()
                .img(Base64.encode(os.toByteArray()))
                .uuid(uuid)
                .build()
        );
    }

}
